using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PACKETPARSERMAX20.CORE;
using PACKETPARSERMAX20.CORE.UPDATEFIELDS;

namespace PACKETPARSERMAX20.CORE.DEFINITIONS
{
    public class SMSG_UPDATE_OBJECT_DEF : DefinitionBase
    {
        public override bool Process()
        {
            ResetPosition();
            Process_Internal();
            return Validate();
        }


        public void Process_Internal()
        {
            //H:\DATA\SQLITE\dbs\11403\MIHASYA_02-02-2010 18-13-11.sqlite
            //index: 22382
            ResetPosition();
            long save = base.Stream.Position;
            base.Stream.Position = save;

            try
            {

                var blockcount = ReadUInt32();

                if (ClientBuild < 9183)
                {
                    var unk1 = ReadByte();
                }

                for (int i = 0; i < blockcount; i++)
                {
                    //if (i == 270)
                    //{
                    //    i = i;
                    //}
                    UInt64 guid;
                    OBJECT_UPDATE_TYPE type = (OBJECT_UPDATE_TYPE)ReadByte();
                    WoWObjectBase obj = null;

                    switch (type)
                    {
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_VALUES:
                            guid = popGuid();
                            obj = Core.GetObject(guid);
                            _ValuesUpdate(obj, guid);
                            if (obj == null)
                            {
                                Console.WriteLine("obj is null");
                            }
                            else
                            {
                                obj.UpdateData(type);
                            }
                            break;
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_MOVEMENT:
                            guid = popGuid();
                            obj = Core.GetObject(guid);
                            _MovementUpdate(obj.TypeID, guid);
                            obj.UpdateData(type);

                            break;
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_CREATE_OBJECT2:
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_CREATE_OBJECT:
                            _CreateObject(type);

                            break;
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_OUT_OF_RANGE_OBJECTS:
                            UInt32 size = ReadUInt32();
                            for (long j = 0; j < size; j++)
                            {
                                guid = popGuid();
                                Core.RemoveObject(guid);
                            }
                            break;
                        case OBJECT_UPDATE_TYPE.UPDATETYPE_NEAR_OBJECTS:

                            UInt32 nsize = ReadUInt32();
                            for (long j = 0; j < nsize; j++)
                            {
                                guid = popGuid();
                            }
                            break;
                        default:
                            //System.out.println(ByteBufferConverter.getHexRepresentation(buffer));
                            throw new Exception("Error: unknown updatetype " + type);
                    }

                }

            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
            }
        }

        private void _CreateObject(OBJECT_UPDATE_TYPE updatetype)
        {
            var save = base.Stream.Position;
            base.Stream.Position = save;

            var guid = popGuid();

            var objectType = (TypeID)ReadByte();
            var obj = Core.CreateOrGetObject(guid, objectType);

            _MovementUpdate(obj, objectType, guid);
            _ValuesUpdate(obj, guid);

            if (!Core.ObjList.ContainsKey(guid))
                Core.ObjList.Add(obj.Guid, obj);

            obj.UpdateData(updatetype);
        }

        private void _ValuesUpdate(UInt64 guid)
        {
            WoWObjectBase obj = Core.GetObject(guid);
            _ValuesUpdate(obj, guid);
        }

        private void _ValuesUpdate(WoWObjectBase pObj, UInt64 guid)
        {
            var save = base.Stream.Position;
            base.Stream.Position = save;

            byte blocksCount = ReadByte();
            var updatemask = new int[blocksCount];

            for (int i = 0; i < updatemask.Length; ++i)
                updatemask[i] = ReadInt32();

            var mask = new System.Collections.BitArray(updatemask);
            if (pObj != null)
            {
                for (int i = 0; i < mask.Count; ++i)
                    if (mask[i])
                    {
                        pObj.Fields[i] = ReadUInt32();
                    }
            }
            else
            {
                for (int i = 0; i < mask.Count; ++i)
                    if (mask[i])
                    {
                        ReadUInt32();
                    }
            }
        }

        private void _MovementUpdate(TypeID objectType, ulong guid)
        {
            WoWObjectBase obj = Core.GetObject(guid);
            _MovementUpdate(obj, objectType, guid);
        }

        private void _MovementUpdate(WoWObjectBase pObj, TypeID objectType, ulong guid)
        {
            var save = base.Stream.Position;
            base.Stream.Position = save;

            MovementInfo movementInfo = pObj.movementInfo;
            movementInfo.guid = guid;


            if (ClientBuild > 9551)
            {
                movementInfo.updateFlags = (OBJECT_UPDATE_FLAGS)ReadUInt16();
            }
            else
            {
                movementInfo.updateFlags = (OBJECT_UPDATE_FLAGS)ReadByte();
            }

            // 0x20
            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_LIVING) != 0)
            {
                movementInfo.movementFlags = (MovementFlags)ReadUInt32();

                if (ClientBuild >= 7561 && ClientBuild < 9183)
                {
                    movementInfo.movementFlags2 = (MovementFlags2)ReadByte();   // unk 2.3.0
                }
                else if (ClientBuild >= 9183)
                {
                    movementInfo.movementFlags2 = (MovementFlags2)ReadUInt16();
                }

                movementInfo.timeStamp = ReadUInt32();

                movementInfo.positionInfo = ReadCoord4();

                //movementInfo.movementFlags.DumpFlags();

                if ((movementInfo.movementFlags & MovementFlags.MOVEFLAG_ONTRANSPORT) != 0)
                {
                    movementInfo.transportInfo = ReadMovementTransport();

                    if (movementInfo.movementFlags2.Value.HasFlag(MovementFlags2.MOVEFLAG2_INTERP_MOVEMENT))
                        movementInfo.transportInfo.TransportTime2 = ReadUInt32();
                }

                if (movementInfo.movementFlags.Value.HasAnyFlag(MovementFlags.MOVEFLAG_SWIMMING | MovementFlags.MOVEFLAG_FLYING) || movementInfo.movementFlags2.Value.HasFlag(MovementFlags2.MOVEFLAG2_ALLOW_PITCHING))
                {
                    movementInfo.swimPitch = ReadFloat();
                }

                movementInfo.fallTime = ReadUInt32();

                if (movementInfo.movementFlags.Value.HasFlag(MovementFlags.MOVEFLAG_FALLING))
                {
                    movementInfo.jumpInfo = ReadMovementJump();
                }

                if (movementInfo.movementFlags.Value.HasFlag(MovementFlags.MOVEFLAG_SPLINE_ELEVATION))
                {
                    movementInfo.splineelevation = ReadFloat();
                }

                if (ClientBuild >= 9183)
                    movementInfo.speeds = new SortedList<string, float>(9);
                else
                    movementInfo.speeds = new SortedList<string, float>(8);

                for (byte i = 0; i < movementInfo.speeds.Capacity; ++i)
                    movementInfo.speeds.Add(i.ToString(), ReadFloat());



                if (movementInfo.movementFlags.Value.HasFlag(MovementFlags.MOVEFLAG_SPLINE_ENABLED))
                {
                    movementInfo.splineInfo.splineFlags = (SplineFlags)ReadUInt32();


                    //if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALFACING))                         // may be orientation
                    //{
                    //    movementInfo.splineInfo.splineRotation = ReadFloat();
                    //}
                    //else
                    //{
                    //    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALPOINT))                       // probably x,y,z coords there
                    //    {
                    //        movementInfo.splineInfo.splinePoint = ReadCoord3();
                    //    }

                    //    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALTARGET))                             // probably guid there
                    //    {
                    //        movementInfo.splineInfo.splineGuid = ReadInt64();
                    //    }
                    //}


                    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALPOINT))
                    {
                        movementInfo.splineInfo.splinePoint = ReadCoord3();
                    }

                    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALTARGET))
                    {
                        movementInfo.splineInfo.splineGuid = ReadInt64();
                    }

                    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_FINALFACING))
                    {
                        movementInfo.splineInfo.splineRotation = ReadFloat();
                    }

                    if (movementInfo.splineInfo.splineFlags.HasFlag(SplineFlags.SPLINEFLAG_CATMULLROM))
                    {
                        movementInfo.splineInfo.splineRotation = ReadFloat();
                    }

                    movementInfo.splineInfo.splineCurTime = ReadUInt32();
                    movementInfo.splineInfo.splineFullTime = ReadUInt32();
                    movementInfo.splineInfo.splineUnkInt321 = ReadUInt32();

                    if (ClientBuild >= 9767)
                    {

                        movementInfo.splineInfo.splineDurationMultiplier = ReadFloat();//3.1
                        movementInfo.splineInfo.splineUnitInterval = ReadFloat();//3.1
                        movementInfo.splineInfo.splineUnkFloat2 = ReadFloat();//3.1
                        movementInfo.splineInfo.splineHeightTime = ReadUInt32(); //3.1
                    }
                    else
                    {
                        if ((movementInfo.splineInfo.splineFlags & SplineFlags.SPLINEFLAG_FINALFACING) != 0)
                        {
                            movementInfo.splineInfo.splineUnkFloat2 = ReadFloat();
                        }
                    }

                    movementInfo.splineInfo.splineCount = ReadUInt32();
                    movementInfo.splineInfo.splines = new SortedList<int, Coord3>();

                    if (movementInfo.splineInfo.splineCount > 1000)
                    {
                        new Exception("movementInfo.splineInfo.splineCount more then 1000.");
                    }

                    for (int i = 0; i < movementInfo.splineInfo.splineCount; ++i)
                    {
                        movementInfo.splineInfo.splines.Add(i, ReadCoord3());
                    }

                    if (ClientBuild >= 9464)
                    {
                        movementInfo.splineInfo.splineMode = (SplineMode)ReadByte();  // added in 3.0.8
                    }

                    movementInfo.splineInfo.splineEndPoint = ReadCoord3();
                }
            }
            else
            {
                // 0x100
                if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_POSITION) != 0)
                {
                    movementInfo.guid_0x100 = popGuid();
                    movementInfo.positionInfo_0x100 = ReadCoord3();
                    movementInfo.positionInfo_0x100_2 = ReadCoord4();
                    movementInfo.unkf_0x100 = ReadFloat();
                }
                else
                {
                    // 0x40
                    if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_HAS_POSITION) != 0)
                    {
                        movementInfo.positionInfo_0x40 = ReadCoord4();
                    }
                }
            }

            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_LOWGUID) != 0)
            {
                movementInfo.lowGuid = ReadInt32();
            }

            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_HIGHGUID) != 0)
            {
                movementInfo.highGuid = ReadInt32();
            }

            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_HAS_TARGET) != 0)
            {
                movementInfo.fullGuid = popGuid();
            }

            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_TRANSPORT) != 0)
            {
                movementInfo.transportTime = ReadUInt32();
            }

            if (ClientBuild >= 9183 && (movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_VEHICLE) != 0)
            {
                movementInfo.vehicleId = ReadInt32();
                movementInfo.facingAdjustement = ReadFloat();
            }

            if ((movementInfo.updateFlags & OBJECT_UPDATE_FLAGS.UPDATEFLAG_ROTATION) != 0)
            {
                movementInfo.rotationGuid = ReadInt64();
            }

            pObj.movementInfo = movementInfo;

            if (objectType == TypeID.TYPEID_UNIT)
            {
                GlobalCache.AddUnitMovement(guid, pObj.movementInfo);
            }
        }

    }

}
